import numpy as np
from Myreadfile import Myreadfile
from QAPModel import QAPModel
import random   # 固定随机数种子，复现查找问题
random.seed(1)
np.random.seed(1)


class MyPaticle():
    """
    MyPaticle 定义粒子类

    [extended_summary]
    """
    best = float('inf')   # 自身最优的位置
    best_val = float('inf')  # 自身最优的值

    v = 0

    def __init__(self, model):
        N = model.N
        self.position = np.linspace(1, N, N, dtype=np.int)
        np.random.shuffle(self.position)
        if model.val(self.position) < self.best:
            self.best = self.position
            self.best_val = model.val(self.position)
        # 到此处测试完成
        self.v = np.eye(N)  # 初始化最优速度为0


class MyPSO():
    best_g = float('inf')   # 群体最优的位置
    best_g_val = float('inf')  # 群体最优的值
    jishu = 0  # 用来打印Log日志

    def __init__(self, path, number_paticle=1000, Iterations=50):
        """[summary]

        Args:
            N ([int]): [初始化例子的数量]
        """
        # 这里传入的path没有修改，只是为了调试方便，正式发布需要修改##############################
        self.model = QAPModel(path)
        # best_answer = self.model.val(
        #     [1, 2, 13, 8, 9, 4, 3, 14, 7, 11, 10, 15, 6, 5, 12])
        # print("最优答案为", best_answer)
        self.N = self.model.N
        self.paticle_dict = {}
        for i in range(number_paticle):
            self.paticle_dict['paticle' + str(i)] = MyPaticle(self.model)
            if self.paticle_dict['paticle' + str(i)].best_val < self.best_g_val:
                self.best_g_val = self.paticle_dict['paticle' +
                                                    str(i)].best_val
                self.best_g = self.paticle_dict['paticle' + str(i)].best
        # 测试完毕
        # 个体的最优值与位置在各自的属性中，全体的最优值在MyPSO中
        for i in range(Iterations):  # 迭代次数
            self.move_v1(c1=2, c2=2)

    # 速度减法，测试完毕

    def find_v(self, b, a):
        ans = []
        v = np.eye(len(a))
        for i in range(len(a)):
            a_index = np.argwhere(a == (i+1))
            b_index = np.argwhere(b == (i+1))
            temp = np.eye(len(a))
            temp[[a_index, b_index], :] = temp[[b_index, a_index], :]
            if np.any(temp != np.eye(len(a))):
                ans = [temp]+ans
            v = np.dot(temp, v)
            a = np.dot(temp, a)
        # ans中的元素表表行变换，每个元素都代表一次行变换
        return ans

    def move_v1(self, c1=0.9, c2=0.9):
        #　公式
        # vi+1 = vi+c1*rand()*(p_best-xi)+c_2*rand()*(g_best-xi)
        # xi+1 = xi+vi+1
        # c1'和c2'

        for i in self.paticle_dict.keys():
            # 做减法得到速度，即交互操作
            # 记忆项
            v_0 = self.paticle_dict[i].v
            # 自身认知项
            v_1_temp = self.find_v(
                self.paticle_dict[i].best, self.paticle_dict[i].position)
            # 群体认知项
            v_2_temp = self.find_v(self.best_g, self.paticle_dict[i].position)
            # c1_c*v1+c2_c*v2
            c1_c = np.random.random(size=[len(v_1_temp), 1])
            c2_c = np.random.random(size=[len(v_2_temp), 1])
            v_1 = np.eye(self.model.N)
            for j in range(len(v_1_temp)):
                if c1_c[j] <= c1:
                    v_1 = np.dot(v_1_temp[j], v_1)  # 小于c则保留操作
            v_2 = np.eye(self.model.N)
            for j in range(len(v_2_temp)):
                if c2_c[j] <= c2:
                    v_2 = np.dot(v_2_temp[j], v_2)  # 小于c则保留操作

            v = np.dot(v_0, v_1)  # 速度加法
            v = np.dot(v, v_2)
            self.paticle_dict[i].v = v  # 更新记忆速度
            # x = x+v
            self.paticle_dict[i].position = np.dot(
                v, self.paticle_dict[i].position)
            self.paticle_dict[i].position = np.array([
                int(i) for i in self.paticle_dict[i].position])
            temp_val = self.model.val(self.paticle_dict[i].position)
            # 更新粒子个体
            if temp_val < self.paticle_dict[i].best_val:
                self.paticle_dict[i].best = self.paticle_dict[i].position
                self.paticle_dict[i].best_val = temp_val
            # 更新全局
            if temp_val < self.best_g_val:
                self.best_g_val = temp_val
                self.best_g = self.paticle_dict[i].position

        self.jishu += 1
        print("第{}次迭代，当前最优位置为{},最小值为{}".format(
            self.jishu, self.best_g, self.best_g_val))

        # R_1, R_2 = random.random(), random.random()  # 0~1之间的随机数
        # goal_best_val = float('inf')   # 更新过程中全局最优值的变换
        # goal_best = float('inf')    # 更新过程中全局最优位置的变换
        # for i in self.paticle_dict.keys():
        #     temp_1 = self.paticle_dict[i].v  # 记忆项
        #     temp_2 = c1*R_1 * \
        #         (self.paticle_dict[i].best -
        #          self.paticle_dict[i].position)  # 自身认知项
        #     temp_3 = +c2*R_2 * \
        #         (self.best_g-self.paticle_dict[i].position)  # 群体认知项
        #     self.paticle_dict[i].v = temp_1+temp_2+temp_3  # 速度变换
        #     self.paticle_dict[i].position = self.paticle_dict[i].position + \
        #         self.paticle_dict[i].v  # 更换位置
        #     self.paticle_dict[i].position = [
        #         round(i) for i in self.paticle_dict[i].position]
        #     temp_val = self.model.val(self.paticle_dict[i].position)
        #     # 更新粒子个体
        #     if temp_val < self.paticle_dict[i].best_val:
        #         self.paticle_dict[i].best = self.paticle_dict[i].best
        #         self.paticle_dict[i].best_val = temp_val
        #     # 更新全局
        #     if temp_val < goal_best_val:
        #         goal_best_val = temp_val
        #         goal_best = self.paticle_dict[i].position


# Asdada = MyPSO("./Nugent15/data/nug15.dat")
